/** @file         led_dev.c
 *  @brief        led设备文件
 *  @details      基于驱动模块的方式实现 设备注册/注销等等操作。平台设备。
 *  @author       lzm
 *  @date         2021-03-29 20:20:03
 *  @version      v1.0
 *  @copyright    Copyright By lizhuming, All Rights Reserved
 *
 **********************************************************
 *  @LOG 修改日志:
 **********************************************************
*/


#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/platform_device.h>

/* 
 * 步骤注释声明
 * [module]：模块框架
 * [bus_dev]：总线设备框架。
 * [bus_drv]：总线驱动框架。
 * 总线设备框架和总线驱动框架都是属于内核的架子，和模块框架一个性质。 
 */

/* 设备资源内容 */
/* 宏内容 */
/* red led */
#define CCM_CCGR1                         0x020C406C // clock 控制寄存器
#define IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO04  0x020E006C // GPIO1_04 复用功能选择寄存器
#define IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO04  0x020E02F8 // PAD 属性值设置寄存器
#define GPIO1_GDIR                        0x0209C004 // GPIO 方向设置寄存器
#define GPIO1_DR                          0x0209C000 // GPIO 输出状态寄存器

/* 资源接口 */
/* red led */
static struct resource red_led_resource[] =
{
    [0] = DEFINE_RES_MEM(CCM_CCGR1, 4),
    [1] = DEFINE_RES_MEM(IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO04, 4),
    [2] = DEFINE_RES_MEM(IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO04, 4),
    [3] = DEFINE_RES_MEM(GPIO1_GDIR, 4),
    [4] = DEFINE_RES_MEM(GPIO1_DR, 4),
};

/* led 私有资源。快速定位 pin */
unsigned int red_hw_info[2] = {2, 26}; // {pin 偏移量, clock 偏移量}

/* [bus_dev][1] */
/* 实现设备结构体内容 */
/** @brief  led_dev_release 该函数在设备被注销时执行
  * @param  
  * @retval
  * @author lzm
  */
static void led_dev_release(struct device *dev)
{
    printk("%s-%s\n", __FILE__, __func__); // 打印文件名及函数
}

/* [bus_dev][2] */
/* 填充平台设备结构体 */
static struct platform_device red_led_pdev =
{
    .name = "led_pdev", // 与该平台驱动匹配
    .id = 1, // 设备编号。同名下，不可同 id
    .num_resources = ARRAY_SIZE(red_led_resource), // ARRAY_SIZE 宏用于计算一个数组中的二级数据的个数。
    .resource = red_led_resource,
    .dev = 
    {
        .release = led_dev_release,
        .platform_data = red_hw_info,
    },
};

/* [bus_dev][3] */
/* 属性文件内容 */

/* 实现 show 回调函数，让用户能通过 cat 命令获取该设备资源 id */
/** @brief  red_led_dev_show
  * @param  
  * @retval 
  * @author lzm
  */
static char *red_dev_name = "red_led"; /* 资源名称 */
ssize_t red_led_dev_show(struct device *dev, struct device_attribute *attr, char *buf)
{
    return sprintf(buf, "%s\n", red_dev_name); // 拷贝到 buf 中
}
/* 实现 store 回调函数，对于 echo 命令。 */
/** @brief  _dev_store
  * @param  
  * @retval 
  * @author lzm
  */
ssize_t red_led_dev_store(struct device * dev, struct device_attribute * attr, const char *buf, size_t count)
{
    sprintf(red_dev_name, "%s\n", buf); // 拷贝到 red_dev_name 中
    return count;
}
/* 设置该文件 名称、属性等等 */
DEVICE_ATTR(red_attr, S_IRUSR|S_IWUSR, red_led_dev_show, red_led_dev_store);

/* [module][1] */
/** @brief   led_dev_init
  * @details led dev module 入口函数
  * @param  
  * @retval 
  * @author lzm
  */
static int __init led_dev_init(void)
{
    printk("led_dev init!\n");
    
    /* [bus_dev][4] 注册平台设备 */
    platform_device_register(&red_led_pdev); // red led
    
    /* [bus_dev][5] 创建属性文件 */
    device_create_file(&(red_led_pdev.dev), &dev_attr_red_attr);

    return 1;
}
module_init(led_dev_init);

/* [module][2] */
/** @brief   led_dev_exit
  * @details led dev module 出口函数
  * @param  
  * @retval 
  * @author  lzm
  */
static void __exit led_dev_exit(void)
{
    printk("led_dev exit!\n");

    /* [bus_dev][6] 删除属性文件 */
    device_remove_file(&red_led_pdev.dev, &dev_attr_red_attr);

    /* [bus_dev][7] 注销平台设备 */
    platform_device_unregister(&red_led_pdev);
}
module_exit(led_dev_exit);

/* [module][3] */
MODULE_AUTHOR("lizhuming");
MODULE_LICENSE("GPL");


